package com.jzoom.zoom.pay.alipay;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;

import com.citywithincity.utils.MD5Util;
import com.jzoom.zoom.pay.PayException;



public class AlipayModel {
	private static final String QUERY_URL = "https://mapi.alipay.com/gateway.do?service=single_trade_query&sign=";

	private static final Log logger = LogFactory.getLog(AlipayModel.class);

	private static final String ENCODING = "utf-8";

	// 安全教研吗
	private String safeKey;
	private String partner;
	private String seller;

	private String privateKey;

	/**
	 * 阿里的public key
	 */
	private String publicKey;

	public AlipayModel(String partner, String seller, String privateKey, String safeKey, String publicKey) {
		this.partner = partner;
		this.seller = seller;
		this.privateKey = privateKey;
		this.safeKey = safeKey;
		this.publicKey = publicKey;
	}

	/**
	 * 预支付
	 * 
	 * @param outId
	 *            商户id
	 * @param title
	 *            订单名称
	 * @param fee
	 *            订单金额（单位元）
	 * @param notifyUrl
	 * @return
	 * @throws PayException
	 * @throws IOException
	 */
	public String prePay(String outId, String title, double fee, String notifyUrl) throws PayException, IOException {
		StringBuilder sb = new StringBuilder(512);
		getOrderInfo(sb, title, fee, outId, notifyUrl);
		return getFinalString(sb);
	}

	/**
	 * 从服务器后台通知中解析订单信息
	 * 
	 * @param request
	 * @return
	 * @throws PayException
	 * @throws UnsupportedEncodingException
	 */
	@SuppressWarnings("unchecked")
	public AlipayNotifyInfo onAliServerNotify(HttpServletRequest request) throws PayException, UnsupportedEncodingException {
		// 验证有效性
		Map<String, String> params = new HashMap<String, String>();
		Map<String, String[]> requestParams = request.getParameterMap();
		for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
			String name = (String) iter.next();
			String[] values = (String[]) requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
			}
			// 乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
			// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
			params.put(name, valueStr);
		}

		logger.info("支付宝通知回调:" + params);
		// 获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
		// 商户订单号
		String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");
		// 支付宝交易号

		String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");
		// 交易状态
		String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");

		if (verify(params, partner,publicKey)) {

			if (trade_status.equals("TRADE_FINISHED") || trade_status.equals("TRADE_SUCCESS")) {
				AlipayNotifyInfo orderInfo = new AlipayNotifyInfo(out_trade_no,trade_no);
				
				return orderInfo;
			}
		}
		return null;
	}

	/**
	 * 获取远程服务器ATN结果
	 * 
	 * @param urlvalue
	 *            指定URL路径地址
	 * @return 服务器ATN结果 验证结果集： invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空 true
	 *         返回正确信息 false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
	 */
	private static String checkUrl(String urlvalue) {
		String inputLine = "";

		try {
			URL url = new URL(urlvalue);
			HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
			BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
			inputLine = in.readLine().toString();
		} catch (Exception e) {
			e.printStackTrace();
			inputLine = "";
		}

		return inputLine;
	}

	/**
	 * 支付宝消息验证地址
	 */
	private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&";

	private static final int CONNECT_TIMEOUT = 6000;

	private static final int READ_TIMEOUT = 6000;

	private static String verifyResponse(String partner, String notify_id) {
		// 获取远程服务器ATN结果，验证是否是支付宝服务器发来的请求

		String veryfy_url = HTTPS_VERIFY_URL + "partner=" + partner + "&notify_id=" + notify_id;

		return checkUrl(veryfy_url);
	}
	
	
	 /**
     * 根据反馈回来的信息，生成签名结果
     * @param Params 通知返回来的参数数组
     * @param sign 比对的签名结果
     * @return 生成的签名结果
     */
	private static boolean getSignVeryfy(Map<String, String> Params, String sign,String aliPublicKey) {
    		//过滤空值、sign与sign_type参数
		Map<String, String> sParaNew = AlipayCore.paraFilter(Params);
        //获取待签名字符串
        String preSignStr = AlipayCore.createLinkString(sParaNew);
        //获得签名验证结果
        return RSA.verify(preSignStr, sign, aliPublicKey);
    }
	

	public static boolean verify(Map<String, String> params, String partner,String aliPublicKey) {

		// 判断responsetTxt是否为true，isSign是否为true
		// responsetTxt的结果不是true，与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
		// isSign不是true，与安全校验码、请求时的参数格式（如：带自定义参数等）、编码格式有关
		String responseTxt = "true";
		if (params.get("notify_id") != null) {
			String notify_id = params.get("notify_id");
			responseTxt = verifyResponse(partner, notify_id);
		}
		String sign = "";
		if (params.get("sign") != null) {
			sign = params.get("sign");
		}
		boolean isSign = getSignVeryfy(params, sign,aliPublicKey);

		// 写日志记录（若要调试，请取消下面两行注释）
		// String sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign +
		// "\n 返回回来的参数：" + AlipayCore.createLinkString(params);
		// AlipayCore.logResult(sWord);

		if (isSign && responseTxt.equals("true")) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 验证前端通知的订单合法性
	 * 
	 * @param id
	 * @param src
	 * @return
	 * @throws PayException
	 * @throws IOException
	 */
	public boolean isSuccess(String id, String src) throws PayException, IOException {
		src = URLDecoder.decode(src, "UTF-8");
		byte[] bytes = Base64.decodeBase64(src);
		String alipayInfo = new String(bytes, "UTF-8");
		String target = alipayInfo.substring(0, alipayInfo.indexOf("&sign_type="));
		String sign = alipayInfo.substring(alipayInfo.indexOf(SIGN) + SIGN.length() + 1);
		sign = sign.substring(0, sign.length() - 1);
		// 这里验证
		if (RSA.verify(target, sign, publicKey)) {
			String outId = getPart(alipayInfo, "&out_trade_no=");
			if (id.equals(outId)) {
				return true;
			}
			throw new PayException("订单id与信息中的订单id不一致", null);
		}
		throw new PayException("签名错误", null);
	}

	public AlipayNotifyInfo parse(String id, String src) throws PayException {
		try {
			src = URLDecoder.decode(src, "UTF-8");
			byte[] bytes = Base64.decodeBase64(src);
			String alipayInfo = new String(bytes, "UTF-8");
			
			logger.info("支付宝客户端信息"+alipayInfo);
			
			String target = alipayInfo.substring(0, alipayInfo.indexOf("&sign_type="));
			String sign = alipayInfo.substring(alipayInfo.indexOf(SIGN) + SIGN.length() + 1);
			sign = sign.substring(0, sign.length() - 1);
			// 这里验证
			String outId = getPart(alipayInfo, "&out_trade_no=");
			if(RSA.verify(target, sign, publicKey)){
				if (!id.equals(outId)) {
					// 获取notify_id
					throw new PayException(PayException.ORDERID_NOT_SAME,"订单id与信息中的订单id不一致");
				}
				return new AlipayNotifyInfo(outId, null);
			}
			throw new PayException(PayException.ORDER_NOT_PAID,"订单未支付成功");
			
		} catch (IOException e) {
			logger.error(e);
			throw new PayException(PayException.ENCODING_NOT_SUPPORT,"编码不支持");
		}

	}

	private boolean isSuccess(Map<String, Object> data) {
		return "T".equals(data.get("is_success")) && "TRADE_SUCCESS".equals(data.get("trade_status"));
	}

	
	private String getFinalString(StringBuilder sb) throws PayException {
		// 对订单做RSA 签名
		String sign = sign(sb.toString());
		try {
			// 仅需对sign 做URL编码
			sign = URLEncoder.encode(sign, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new PayException(PayException.ENCODING_NOT_SUPPORT,"编码不支持");
		}
		sb.append("&sign=\"").append(sign).append("\"&sign_type=\"RSA\"");
		return sb.toString();
	}

	private String sign(String content) {
		return RSA.sign(content, privateKey);
	}

	/**
	 * 
	 * 暂时不支持
	 * 
	 * @param outId
	 *            商户订单号
	 * @return
	 * @throws IOException
	 */
	public Map<String, Object> queryOrder(String outId) throws IOException {

		Map<String, String> data = new TreeMap<String, String>();
		data.put("service", "single_trade_query");
		data.put("partner", partner);
		data.put("_input_charset", ENCODING);
		data.put("out_trade_no", outId);
		StringBuilder sb = new StringBuilder(200);
		boolean first = true;
		for (Entry<String, String> entry : data.entrySet()) {
			if (first) {
				first = false;
			} else {
				sb.append('&');
			}
			sb.append(entry.getKey()).append('=').append(entry.getValue());
		}
		String sign = MD5Util.md5Appkey(sb.append(safeKey).toString());
		sb.setLength(0);
		sb.append(QUERY_URL).append(sign).append("&out_trade_no=").append(outId).append("&partner=").append(partner)
				.append("&sign_type=MD5&_input_charset=utf-8");
		// String url = URLEncoder.encode(sb.toString(), "UTF-8");
		HttpURLConnection connection = null;
		InputStream inputStream = null;
		try {
			logger.info(sb.toString());
			connection = createConnection(sb.toString());
			inputStream = new BufferedInputStream(connection.getInputStream());

			String content = readContent(inputStream, "UTF-8");
			logger.info(content);

			Map<String, Object> xml = parseXML(content);
			logger.info(xml);

			return xml;
		}  finally {
			if(inputStream!=null) {
				try {inputStream.close();}catch (Throwable e) {}
			}
			if (connection != null)
				connection.disconnect();
		}
	}
	
	public static String readContent(InputStream inputStream,String encoding) throws IOException{
		BufferedReader reader =null;
		try{
			reader = new BufferedReader(new InputStreamReader(inputStream,encoding));
			StringBuilder sb = new StringBuilder();
			String line = null;
			while((line=reader.readLine())!=null){
				sb.append(line);
			}
			return sb.toString();
		}finally{
			if(reader!=null) {
				try {reader.close();}catch (Throwable e) {}
			}
		}
	}


	/**
	 * 
	 * @param is
	 * @param isTree
	 * @param encoding
	 * @return
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 */
	public static Map<String, Object> parseXML(String content) throws IOException {
		
		return null;
	}


	private static HttpURLConnection createConnection(String url) throws IOException {
		URL postUrl = new URL(url);
		HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();
		connection.setDoOutput(true);
		connection.setDoInput(true);
		connection.setRequestMethod("GET");
		connection.setUseCaches(false);
		connection.setInstanceFollowRedirects(true);
		connection.setChunkedStreamingMode(5);
		connection.setConnectTimeout(CONNECT_TIMEOUT);
		connection.setReadTimeout(READ_TIMEOUT);
		connection.connect();
		return connection;
	}

	/**
	 * create the order info. 创建订单信息
	 * 
	 */
	private void getOrderInfo(StringBuilder sb, String subject, double price, String outID, String notifyUrl) {
		// 签约合作者身份ID
		sb.append("partner=").append('\"').append(partner).append('\"')

				// 签约卖家支付宝账号
				.append("&seller_id=").append('\"').append(seller).append('\"')

				// 商户网站唯一订单号
				.append("&out_trade_no=").append('\"').append(outID).append('\"')

				// 商品名称
				.append("&subject=").append('\"').append(subject).append('\"')

				// 商品详情
				// .append( "&body=" ).append( '\"' ).append( body ).append(
				// '\"')

				// 商品金额
				.append("&total_fee=").append('\"').append(price).append('\"')

				// 服务器异步通知页面路径
				.append("&notify_url=").append('\"').append(notifyUrl).append('\"')

				// 服务接口名称， 固定值
				.append("&service=\"mobile.securitypay.pay\"")

				// 支付类型， 固定值
				.append("&payment_type=\"1\"")

				// 参数编码， 固定值
				.append("&_input_charset=\"utf-8\"")

				// 设置未付款交易的超时时间
				// 默认30分钟，一旦超时，该笔交易就会自动被关闭。
				// 取值范围：1m～15d。
				// m-分钟，h-小时，d-天，1c-当天（无论交易何时创建，都在0点关闭）。
				// 该参数数值不接受小数点，如1.5h，可转换为90m。
				.append("&it_b_pay=\"30m\"")

				// extern_token为经过快登授权获取到的alipay_open_id,带上此参数用户将使用授权的账户进行支付
				// .append( "&extern_token=" ).append( '\"' ).append(
				// extern_token ).append( '\"';

				// 支付宝处理完请求后，当前页面跳转到商户指定页面的路径，可空
				.append("&return_url=\"m.alipay.com\"");

		// 调用银行卡支付，需配置此参数，参与签名， 固定值 （需要签约《无线银行卡快捷支付》才能使用）
		// .append( "&paymethod=\"expressGateway\"";

	}

	private static final String SIGN = "&sign=";

	private static String getPart(String info, String part) {
		String sign = info.substring(info.indexOf(part) + part.length() + 1);
		sign = sign.substring(0, sign.indexOf("\""));
		return sign;
	}
}
